From: Florian Müllner Date: Wed, 22 Oct 2025 00:59:20 +0000 (+0200) Subject: errorUtils: Add logErrorUnlessCancelled() helper X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~66^2^2~36 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=b129de65252e17122aa0408090400bcb5d2eb0f4;p=gnome-shell.git errorUtils: Add logErrorUnlessCancelled() helper It is quite common to handle errors by ignoring them in case of CANCELLED, and otherwise log them via logError(). Add a small helper method that implements that behavior, including if the original error is wrapped in a Gio.DBusError. Part-of: (cherry picked from commit b4b739dc5193cab1e6fc48ce1e9d631a7aa2c1f0) --- diff --git a/js/misc/errorUtils.js b/js/misc/errorUtils.js index 369a06b02..55e8acf66 100644 --- a/js/misc/errorUtils.js +++ b/js/misc/errorUtils.js @@ -1,4 +1,6 @@ // Common code for displaying errors to the user in various dialogs +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; function formatSyntaxErrorLocation(error) { const {fileName = '', lineNumber = 0, columnNumber = 0} = error; @@ -64,3 +66,26 @@ export function formatError(error, {showStack = true} = {}) { return `(could not display error: ${e})`; } } + +/** + * Logs a stack trace for `error` with an optional prefix, unless the error + * matches `Gio.IOErrorEnum.CANCELLED`. + * + * @param {Error} error - the error to log + * @param {string?} prefix - optional prefix for the message + * + * @returns {boolean} whether the error was logged + */ +export function logErrorUnlessCancelled(error, prefix) { + if (error instanceof GLib.Error && Gio.DBusError.is_remote_error(error)) { + const errorName = Gio.DBusError.get_remote_error(error); + Gio.DBusError.strip_remote_error(error); + error = Gio.DBusError.new_for_dbus_error(errorName, error.message); + } + + if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) + return false; + + logError(error, prefix); + return true; +} diff --git a/tests/meson.build b/tests/meson.build index 806a0c7ad..ec8a5b4a2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -30,6 +30,7 @@ unit_testenv.append('GI_TYPELIB_PATH', st_typelib_path, separator: ':') unit_tests = { 'breakManager': {}, + 'errorUtils': {}, 'extensionUtils': {}, 'fileUtils': { 'env': { diff --git a/tests/unit/errorUtils.js b/tests/unit/errorUtils.js new file mode 100644 index 000000000..7b79195f7 --- /dev/null +++ b/tests/unit/errorUtils.js @@ -0,0 +1,49 @@ +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; + +import {logErrorUnlessCancelled} from 'resource:///org/gnome/shell/misc/errorUtils.js'; + +describe('logErrorUnlessCancelled()', () => { + let spy; + + beforeEach(() => { + spy = spyOn(globalThis, 'logError'); + }); + + it('logs regular errors', () => { + const e = new Error('Test error'); + expect(logErrorUnlessCancelled(e)).toBeTrue(); + expect(spy).toHaveBeenCalled(); + }); + + it('logs GErrors', () => { + const e = GLib.Error.new_literal( + Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED, 'Failed'); + expect(logErrorUnlessCancelled(e)).toBeTrue(); + expect(spy).toHaveBeenCalled(); + }); + + it('does not log CANCELLED errors', () => { + const e = GLib.Error.new_literal( + Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, 'Cancelled'); + expect(logErrorUnlessCancelled(e)).toBeFalse(); + expect(spy).not.toHaveBeenCalled(); + }); + + it('handles remote errors', () => { + function createDBusError(code, message) { + const e = GLib.Error.new_literal( + Gio.IOErrorEnum, code, message); + return Gio.DBusError.new_for_dbus_error( + Gio.DBusError.encode_gerror(e), message); + }; + + let e = createDBusError(Gio.IOErrorEnum.CANCELLED, 'Cancelled'); + expect(logErrorUnlessCancelled(e)).toBeFalse(); + expect(spy).not.toHaveBeenCalled(); + + e = createDBusError(Gio.IOErrorEnum.FAILED, 'Failed'); + expect(logErrorUnlessCancelled(e)).toBeTrue(); + expect(spy).toHaveBeenCalled(); + }); +});